/**
 * 文件名：@AbstractDynamicDataSource.java <br/>
 * 包名：cn.efunbox.afw.data.spring.support <br/>
 * 项目名：afw-data <br/>
 * @author xtwin <br/>
 */
package cn.efunbox.afw.data.datasource.dynamic;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.sql.DataSource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.util.ReflectionUtils;

import cn.efunbox.afw.core.exception.AfwRuntimeException;
import cn.efunbox.afw.core.holder.StackHolder;
import cn.efunbox.afw.core.util.AfwConstant;

/**
 * 类名：AbstractDynamicDataSource  <br />
 *
 * 功能：动态数据源
 *
 * @author xtwin <br />
 * 创建时间：2016年7月31日 下午2:24:17  <br />
 * @version 2016年7月31日
 */
public abstract class AbstractDynamicDataSource extends AbstractRoutingDataSource {
	
	// 数据源信息持有者
	protected StackHolder<String> dataSourceHolder;
	
	// 默认名称
	protected String defaultName = AfwConstant.NAME_MASTER;
	
	// 分隔符
	protected String separator = "_";
	
	// 数据源名称与其后缀之间对应关系
	protected Map<String, List<String>> mappings;
	
	// 数据源列表
	protected List<DataSource> dataSources = new ArrayList<>();
	
	// 数据源初始化时，使用的方法
	protected String initMethod = "init";
	
	// 数据源销毁时，使用的方法
	protected String destoryMethod = "close";
	
	/**
	 * 功能：初始化 <br/>
	 *
	 * @author xtwin <br/>
	 * @version 2016年8月12日 下午6:34:09 <br/>
	 */
	public void init() {
		for (DataSource ds : dataSources) {
			// 依次执行初始化
			ReflectionUtils.invokeMethod(ReflectionUtils.findMethod(ds.getClass(), initMethod), ds);
		}
	}

	/**
	 * 功能：关闭 <br/>
	 *
	 * @author xtwin <br/>
	 * @version 2016年8月12日 下午6:45:41 <br/>
	 */
	public void close() {
		for (DataSource ds : dataSources) {
			// 依次执行初始化
			ReflectionUtils.invokeMethod(ReflectionUtils.findMethod(ds.getClass(), destoryMethod), ds);
		}
	}
	
	/**
	 * 功能: <br/>
	 * 
	 * 重写：xtwin <br/>
	 * 
	 * @version ：2016年7月31日 下午2:24:30<br/>
	 * 
	 * @return <br/>
	 * @see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey()
	 */
	@Override
	protected Object determineCurrentLookupKey() {
		String name = dataSourceHolder.pick();
		
		if (null == name) {
			name = defaultName;
		}
		
		// 负载处理
		int index = name.lastIndexOf(separator);
		if (index == -1) {
			// 没有分隔符，说明没有配置具体的数据源名称，需要这里进行挑选
			List<String> options = mappings.get(name);
			if (null == options) {
				throw new AfwRuntimeException("dataSource name is invalid : " + name);
			}
			
			// 选择出一个选项
			String option = select(options, name);
			if (null != option) {
				// 组装出真实的数据源名称
				name = new StringBuilder()
						.append(name)
						.append(separator)
						.append(option)
						.toString();
			}
		}
		
		return name;
	}
	
	/**
	 * 功能：选择出一个选项 <br/>
	 *
	 * @author xtwin <br/>
	 * @version 2016年7月31日 下午4:31:02 <br/>
	 */
	protected abstract String select(List<String> options, String name);

	/**
	 * 功能：初始化数据源名称与其后缀的映射关系 <br/>
	 *
	 * @author xtwin <br/>
	 * @version 2016年7月31日 下午4:45:40 <br/>
	 */
	protected Map<String, List<String>> resolveMappings(Map<Object, Object> targetDataSources) {
		Map<String, List<String>> map = new HashMap<>();
		// 自动均衡处理
		for (Entry<Object, Object> entry : targetDataSources.entrySet()) {
			String key = String.valueOf(entry.getKey());
			
			String suffix = null;
			int index = key.lastIndexOf(separator);
			if (index != -1) {
				suffix = key.substring(index + separator.length());
				key = key.substring(0, index);
			}
			
			List<String> list = map.get(key);
			if (null == list) {
				// 数据源一般不会有特别多，所以这里定义默认5个长度
				map.put(key, list = new ArrayList<>(5));
			}
			
			// 保存当前后缀名
			list.add(suffix);
		}
		
		// 保存映射关系
		return map;
	}
	
	/**
	 * 功能: <br/>
	 * 
	 * 重写：xtwin <br/>
	 * 
	 * @version ：2016年7月31日 下午4:10:10<br/>
	 * 
	 * @param targetDataSources <br/>
	 * @see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#setTargetDataSources(java.util.Map)
	 */
	@Override
	public void setTargetDataSources(Map<Object, Object> targetDataSources) {
		super.setTargetDataSources(targetDataSources);
		
		// 保存映射关系
		mappings = Collections.unmodifiableMap(resolveMappings(targetDataSources));
	}
	
	/**
	 * 功能: <br/>
	 * 
	 * 重写：xtwin <br/>
	 * 
	 * @version ：2016年8月12日 下午6:38:03<br/>
	 * 
	 * @param dataSource
	 * @return
	 * @throws IllegalArgumentException <br/>
	 * @see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#resolveSpecifiedDataSource(java.lang.Object)
	 */
	@Override
	protected DataSource resolveSpecifiedDataSource(Object dataSource) throws IllegalArgumentException {
		DataSource ds = super.resolveSpecifiedDataSource(dataSource);
		
		// 保存数据源引用
		dataSources.add(ds);
		
		return ds;
	}

	/**
	 * @version 2016年7月31日-下午4:39:19
	 */
	public void setSeparator(String separator) {
		this.separator = separator;
	}

	/**
	 * @version 2016年8月1日-上午11:02:26
	 */
	public void setDataSourceHolder(StackHolder<String> dataSourceHolder) {
		this.dataSourceHolder = dataSourceHolder;
	}

	/**
	 * @version 2016年7月31日-下午2:29:18
	 */
	public void setDefaultName(String defaultName) {
		this.defaultName = defaultName;
	}
}